001 /* 002 * Copyright 2005 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.tools.impl; 020 021 import java.io.File; 022 import java.net.URI; 023 import java.net.URL; 024 import java.util.Vector; 025 026 import net.dpml.library.Builder; 027 import net.dpml.library.Library; 028 import net.dpml.library.Resource; 029 030 import net.dpml.tools.Context; 031 import net.dpml.tools.info.BuilderDirective; 032 import net.dpml.tools.info.BuilderDirectiveHelper; 033 034 import net.dpml.transit.Artifact; 035 import net.dpml.transit.model.TransitModel; 036 import net.dpml.transit.tools.MainTask; 037 038 import net.dpml.util.Logger; 039 040 import org.apache.tools.ant.Project; 041 import org.apache.tools.ant.ProjectHelper; 042 import org.apache.tools.ant.BuildException; 043 import org.apache.tools.ant.BuildLogger; 044 import org.apache.tools.ant.DefaultLogger; 045 import org.apache.tools.ant.input.DefaultInputHandler; 046 import org.apache.tools.ant.DemuxInputStream; 047 048 /** 049 * The StandardBuilder is a plugin established by the Tools build controller 050 * used for the building of a project based on the Ant build system in conjunction 051 * with Transit plugin management services. 052 * 053 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 054 * @version 1.1.0 055 */ 056 public class StandardBuilder implements Builder 057 { 058 // ------------------------------------------------------------------------ 059 // static 060 // ------------------------------------------------------------------------ 061 062 /** 063 * The default template uri path. 064 */ 065 public static final String DEFAULT_TEMPLATE_URN = "local:template:dpml/tools/standard"; 066 067 /** 068 * The builder configuration. 069 */ 070 public static final BuilderDirective CONFIGURATION = loadConfiguration(); 071 072 private static BuilderDirective loadConfiguration() 073 { 074 try 075 { 076 return BuilderDirectiveHelper.build(); 077 } 078 catch( Throwable e ) 079 { 080 final String error = 081 "Internal error while attempting to establish the builder configuration."; 082 BuilderError be = new BuilderError( error, e ); 083 be.printStackTrace(); 084 return null; 085 } 086 } 087 088 // ------------------------------------------------------------------------ 089 // state 090 // ------------------------------------------------------------------------ 091 092 private Logger m_logger; 093 private TransitModel m_model; 094 private Library m_library; 095 private boolean m_verbose; 096 private Throwable m_result; 097 098 // ------------------------------------------------------------------------ 099 // constructors 100 // ------------------------------------------------------------------------ 101 102 /** 103 * Creation of a new standard builder. 104 * 105 * @param logger assigned logging channel 106 * @param library the library 107 */ 108 public StandardBuilder( Logger logger, Library library ) 109 { 110 this( logger, library, false ); 111 } 112 113 /** 114 * Creation of a new standard builder. 115 * 116 * @param logger assigned logging channel 117 * @param library the library 118 * @param verbose verbose execution flag 119 */ 120 public StandardBuilder( Logger logger, Library library, boolean verbose ) 121 { 122 m_logger = logger; 123 m_verbose = verbose; 124 m_library = library; 125 126 Thread.currentThread().setContextClassLoader( Builder.class.getClassLoader() ); 127 } 128 129 // ------------------------------------------------------------------------ 130 // Builder 131 // ------------------------------------------------------------------------ 132 133 /** 134 * Build the project defined by the supplied resource. 135 * @param resource the project definition 136 * @param targets an array of build target names 137 * @return the build success status 138 */ 139 public boolean build( Resource resource, String[] targets ) 140 { 141 Project project = createProject( resource ); 142 File template = getTemplateFile( resource ); 143 return build( resource, project, template, targets ); 144 } 145 146 /** 147 * Return the template for the resource. 148 * @param resource the project definition 149 * @return the template 150 */ 151 public File getTemplateFile( Resource resource ) 152 { 153 try 154 { 155 String systemOverride = System.getProperty( "project.template" ); 156 String override = resource.getProperty( "project.template", systemOverride ); 157 if( null != override ) 158 { 159 File template = getTemplateFile( override ); 160 return template; 161 } 162 163 File basedir = resource.getBaseDir(); 164 String buildfile = resource.getProperty( "project.buildfile" ); 165 String defaultBuildfile = resource.getProperty( "project.standard.buildfile", "build.xml" ); 166 if( null != buildfile ) 167 { 168 // there is an explicit 'project.buildfile' declaration in which case 169 // we check for existance and fail if it does not exist 170 171 File file = new File( basedir, buildfile ); 172 if( file.exists() ) 173 { 174 return file; 175 } 176 else 177 { 178 final String error = 179 "Resource buildfile [" 180 + file 181 + "] does not exist."; 182 throw new BuildException( error ); 183 } 184 } 185 else if( null != defaultBuildfile ) 186 { 187 // check if a buildfile of the default name exists in the project's 188 // basedir - and if so - use it to build the project 189 190 File file = new File( basedir, defaultBuildfile ); 191 if( file.exists() ) 192 { 193 return file; 194 } 195 } 196 197 // otherwise we build using either an explicit or default template 198 // resolved via a uri (typically a template stored in prefs) 199 200 String defaultTemplateSpec = 201 resource.getProperty( "project.standard.template", DEFAULT_TEMPLATE_URN ); 202 String templateSpec = resource.getProperty( "project.template", defaultTemplateSpec ); 203 204 if( null != templateSpec ) 205 { 206 File template = getTemplateFile( templateSpec ); 207 return template; 208 } 209 else 210 { 211 final String error = 212 "Resource template property 'project.template' is undefined."; 213 throw new BuildException( error ); 214 } 215 } 216 catch( BuildException e ) 217 { 218 throw e; 219 } 220 catch( Throwable e ) 221 { 222 m_result = e; 223 final String error = 224 "Unexpected error while attempting to resolve project template." 225 + "\nResource path: " 226 + resource.getResourcePath(); 227 throw new BuildException( error, e ); 228 } 229 } 230 231 // ------------------------------------------------------------------------ 232 // implementation 233 // ------------------------------------------------------------------------ 234 235 boolean build( Resource resource, Project project, File template, String[] targets ) 236 { 237 try 238 { 239 ProjectHelper helper = (ProjectHelper) project.getReference( "ant.projectHelper" ); 240 241 if( null != template ) 242 { 243 helper.parse( project, template ); 244 } 245 246 Vector vector = new Vector(); 247 248 if( targets.length == 0 ) 249 { 250 if( null != project.getDefaultTarget() ) 251 { 252 vector.addElement( project.getDefaultTarget() ); 253 } 254 else 255 { 256 vector.addElement( CONFIGURATION.getDefaultPhase() ); 257 } 258 } 259 else 260 { 261 for( int i=0; i<targets.length; i++ ) 262 { 263 String target = targets[i]; 264 vector.addElement( target ); 265 } 266 } 267 268 if( vector.size() == 0 ) 269 { 270 final String errorMessage = 271 "No targets requested and no default target declared."; 272 throw new BuildException( errorMessage ); 273 } 274 275 project.executeTargets( vector ); 276 return true; 277 } 278 catch( BuildException e ) 279 { 280 m_result = e; 281 if( m_logger.isDebugEnabled() ) 282 { 283 final String error = 284 "Build failure." 285 + "\nProject: " + resource.getResourcePath() 286 + "\nBasedir: " + resource.getBaseDir() 287 + "\nTemplate: " + template 288 + "\nLocation: " + e.getLocation(); 289 Throwable cause = e.getCause(); 290 m_logger.error( error, cause ); 291 } 292 return false; 293 } 294 finally 295 { 296 project.fireBuildFinished( m_result ); 297 } 298 } 299 300 private File getTemplateFile( String spec ) 301 { 302 try 303 { 304 URI uri = new URI( spec ); 305 if( Artifact.isRecognized( uri ) ) 306 { 307 URL url = uri.toURL(); 308 return (File) url.getContent( new Class[]{File.class} ); 309 } 310 } 311 catch( Throwable e ) 312 { 313 } 314 return new File( spec ); 315 } 316 317 Project createProject( Resource resource ) 318 { 319 try 320 { 321 Project project = newProject(); 322 Context context = new DefaultContext( resource, project ); 323 return project; 324 } 325 catch( Exception e ) 326 { 327 final String error = 328 "Unable to establish build context." 329 + "\nProject: " + resource; 330 throw new BuildException( error, e ); 331 } 332 } 333 334 private Project newProject() 335 { 336 Project project = new Project(); 337 project.setSystemProperties(); 338 project.setDefaultInputStream( System.in ); 339 setupTransitComponentHelper( project ); 340 project.setCoreLoader( getClass().getClassLoader() ); 341 project.addBuildListener( createLogger() ); 342 System.setIn( new DemuxInputStream( project ) ); 343 project.setProjectReference( new DefaultInputHandler() ); 344 ProjectHelper helper = ProjectHelper.getProjectHelper(); 345 project.addReference( "ant.projectHelper", helper ); 346 return project; 347 } 348 349 private void setupTransitComponentHelper( Project project ) 350 { 351 try 352 { 353 MainTask task = new MainTask(); 354 task.setProject( project ); 355 task.init(); 356 task.execute(); 357 } 358 catch( BuildException e ) 359 { 360 throw e; 361 } 362 catch( Exception e ) 363 { 364 final String error = 365 "Setup failure."; 366 throw new BuildException( error, e ); 367 } 368 } 369 370 private BuildLogger createLogger() 371 { 372 BuildLogger logger = new DefaultLogger(); 373 if( m_verbose ) 374 { 375 logger.setMessageOutputLevel( Project.MSG_VERBOSE ); 376 } 377 else 378 { 379 logger.setMessageOutputLevel( Project.MSG_INFO ); 380 } 381 logger.setOutputPrintStream( System.out ); 382 logger.setErrorPrintStream( System.err ); 383 return logger; 384 } 385 386 private Logger getLogger() 387 { 388 return m_logger; 389 } 390 } 391